/**
 * 
 */
package com.cipe.cmrs.validator;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.validation.Errors;

import com.cipe.cmrs.bean.PaymentRequestBean;
import com.cipe.cmrs.bean.PaymentRequestLineItemBean;
import com.cipe.cmrs.exception.QueryException;
import com.cipe.cmrs.helper.PaymentHelper;
import com.cipe.cmrs.model.Cipeproject;
import com.cipe.cmrs.service.CipeprojectService;
import com.cipe.cmrs.service.PaymentrequestService;
import com.cipe.cmrs.service.PaymentrequestlineitemService;
import com.cipe.cmrs.util.DateUtil2;

/**
 * @author Incepio
 * 
 */
@Component
public class PaymentRequestValidator extends AbstractValidator {
    @Autowired
    private CipeprojectService projectService;
    @Autowired
    private PaymentrequestlineitemService lineItemService;
    @Autowired
    private PaymentHelper paymentService;
    @Autowired
    private PaymentrequestService paymentrequestService;

    public void validate(Errors errors, PaymentRequestBean bean) {
        if (!paymentService.hasData(bean.getLineItemBeans())) {
            errors.reject("required.paymentrequest", "A Payment Request MUST have at least 1 line item created/associated");
        } else if (bean.getPaymentTypeId() != null
            && bean.getPaymentTypeId() == Integer.valueOf(getText("id.paymenttype.reclassliquidation"))
            && BigDecimal.ZERO.compareTo(paymentService.getTotalAmount(bean.getLineItemBeans())) != 0) {
            errors.reject("error.notzero.balance.payment.reclass",
                "For payment requests of type 'Reclass/Liquidations', the total balance of all line items MUST be EQUAL to 0.");
        } else if (StringUtils.isNotBlank(bean.getInvoicenumber()) && bean.getInvoiceRequestDate() != null
            && paymentrequestService.isExisting(bean)) {
            Object[] args = new Object[]{ bean.getInvoicenumber(),
                DateUtil2.INSTANCE.formatDate(DateUtil2.INSTANCE.getSodDate(bean.getInvoiceRequestDate())), bean.getPayeeDesc(),
                bean.getAmount() != null ? bean.getAmount().toPlainString() : "" };
            errors.reject("error.payment.duplicate.entry", args,
                String.format("Payment with invoice number %s, invoice date %s, payee %s, amount %s already exists.", args));
        } else {
            Map<Integer, List<PaymentRequestLineItemBean>> map = new HashMap<Integer, List<PaymentRequestLineItemBean>>();
            for (PaymentRequestLineItemBean lineBean : bean.getLineItemBeans()) {
                if (paymentService.hasData(lineBean)) {
                    List<PaymentRequestLineItemBean> list = map.get(lineBean.getCipeProjectId());
                    if (list == null) {
                        list = new ArrayList<PaymentRequestLineItemBean>();
                        map.put(lineBean.getCipeProjectId(), list);
                    }
                    list.add(lineBean);
                }
            }

            try {
                List<String> columns = new ArrayList<String>();
                columns.add("id");
                columns.add("managementFunds");
                columns.add("partnerGrantFunds");
                columns.add("override");
                columns.add("projectTitle");
                columns.add("cipeprojectstatus.id");
                columns.add("cipeprojectstatus.displayName");
                columns.add("projectEndDate");
                columns.add("cipeprojecttype.displayName");
                columns.add("projectTitle");
                for (Integer projId : map.keySet()) {
                    Cipeproject project = projectService.getCipeProject(projId, columns);
                    if (project.getCipeprojectstatus().getId() == Integer.valueOf(getText("id.status.project.active"))) {
                        checkRemainingFunds(errors, map.get(projId), project, bean);
                    } else if (project.getCipeprojectstatus().getId() == Integer.valueOf(getText("id.status.project.over"))
                        && !getText("projecttype.partner").equals(project.getCipeprojecttype().getDisplayName())
                        && project.getProjectEndDate() != null
                        && DateUtil2.INSTANCE.getDateInterval(project.getProjectEndDate(), new Date()) <= 30) {
                        checkRemainingFunds(errors, map.get(projId), project, bean);
                    } else {
                        errors.reject(
                            "error.payment.project.notallowed",
                            new String[]{ project.getProjectTitle(), project.getCipeprojectstatus().getDisplayName() },
                            String.format("You are trying to apply a payment to a %s with a status of %s. "
                                + "This is not allowed. Please contact the Finance or Grants assistant if you have questions",
                                project.getProjectTitle(), project.getCipeprojectstatus().getDisplayName()));
                    }
                }
            } catch (QueryException e) {
				log.error(getText("error.transaction"), e);
                errors.reject("error.transaction");
            }
        }
    }

    private void checkRemainingFunds(Errors errors, List<PaymentRequestLineItemBean> list, Cipeproject project, PaymentRequestBean bean) {
        if (project.getOverride() == null || !project.getOverride()) {
            BigDecimal newTotalAmounts = paymentService.getTotalAmount(list);
            log.debug("New Total Amounts: " + newTotalAmounts);
            BigDecimal deductedAmounts = lineItemService.getProjectTotalAmount(bean.getId(), project.getId());
            log.debug("Deducted Amounts: " + deductedAmounts);
            BigDecimal remainingFunds = BigDecimal.ZERO;
            if (project.getManagementFunds() != null) {
                remainingFunds = project.getManagementFunds();
            }
            if (project.getPartnerGrantFunds() != null) {
                remainingFunds = remainingFunds.add(project.getPartnerGrantFunds());
            }
            log.debug("Initial Project Fund: " + deductedAmounts);
            remainingFunds = remainingFunds.subtract(deductedAmounts);
            log.debug("Final Project Fund: " + remainingFunds.subtract(newTotalAmounts));
            if (remainingFunds.subtract(newTotalAmounts).compareTo(BigDecimal.ZERO) < 0) {
                errors.reject(
                    "error.payment.project.nofunds",
                    new String[]{ project.getProjectTitle() },
                    String.format("You are trying to apply a payment to a %s that does not have funds available. "
                        + "This is not allowed. Please contact the Finance or Grants assistant if you have questions",
                        project.getProjectTitle()));
            }
        }
    }
}
